package io.gitee.sfch.chunmi.common.tool;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;

import java.util.UUID;

/**
 * 密码工具类（加解密）
 */
public abstract class PasswordTool {

    /**
     * 加密
     *
     * @param value 需要加密的值
     */
    public static String encrypt(String value) {
        // 生成内容不同的，长度固定的32位盐值
        String salt = UUID.randomUUID().toString().replace("-", "");
        // MD5(盐值+密码)加密的固定32位字符串
        String valueMd5 = DigestUtil.md5Hex((salt + value).getBytes());
        // 按某种方式
        return doSalt(valueMd5, salt);
    }

    /**
     * 解密校验（将输入值与加密值进行比较，判定是否相等）
     *
     * @param originValue 原始值
     * @param encryptValue 加密的值
     */
    public static boolean decryptEqual(String originValue, String encryptValue) {
        if (StrUtil.isEmpty(originValue) || StrUtil.isEmpty(encryptValue)) {
            return false;
        }
        if (encryptValue.length() != 64) {
            return false;
        }
        String[] dbParams = saltReduction(encryptValue);
        String passwordMd5 = DigestUtil.md5Hex((dbParams[1] + originValue).getBytes());
        return StrUtil.equals(passwordMd5, dbParams[0]);
    }

    /**
     * 将盐和密码按一定规则混合，复杂的规则有利于难以破解，但是前提是混合的方案没有被泄露。
     *
     * @param passwordMd5 密码字符串
     * @param salt 盐值
     * @return 返回一个混合后的盐值，这样可以防止被破解
     */
    private static String doSalt(String passwordMd5, String salt) {
        char[] res = new char[64];
        int arrayIndex = 0;
        int i = 0;
        // 前15位，让这部分密码和盐交叉
        for (; i < 15; i++) {
            res[arrayIndex] = passwordMd5.charAt(i);
            arrayIndex++;
            res[arrayIndex] = salt.charAt(i);
            arrayIndex++;
        }
        // 后15位，密码正放，盐反放
        int lastArrayIndex = 63;
        for (; i < 32; i++) {
            res[arrayIndex] = passwordMd5.charAt(i);
            arrayIndex++;
            res[lastArrayIndex] = salt.charAt(i);
            lastArrayIndex--;
        }
        return new String(res);
    }

    /**
     * 将加密后的字符串按混合的顺序逆向返回盐和字符串
     *
     * @param passwordEncrypt 加密后的字符串
     * @return 返回数组，密码和盐
     */
    private static String[] saltReduction(String passwordEncrypt) {
        char[] password = new char[32];
        char[] salt = new char[32];

        int arrayIndex = 0;
        int i = 0;
        // 前15位，交叉取密码和盐
        for (; i < 15; i++) {
            password[i] = passwordEncrypt.charAt(arrayIndex);
            arrayIndex++;
            salt[i] = passwordEncrypt.charAt(arrayIndex);
            arrayIndex++;
        }
        // 后15位，密码正放，盐反放
        int lastArrayIndex = 63;
        for (; i < 32; i++) {
            password[i] = passwordEncrypt.charAt(arrayIndex);
            arrayIndex++;
            salt[i] = passwordEncrypt.charAt(lastArrayIndex);
            lastArrayIndex--;
        }
        return new String[]{new String(password), new String(salt)};
    }
}
